/*
 * Database.cpp
 *
 *  Created on: 09-Oct-2009
 *      Author: Atul Shukla (atul.shukla@iiitb.net)
 */

#include"Database.h"
#include<string.h>
#include <malloc.h>

int sizeofdbh = (MAX_FILE_NAME + sizeof(int) + sizeof(char));
int noOfFileEnteries = (PAGE_SIZE / sizeof(DBHeader)); //-1 for next header page if this gets full

DBHeader *currentDBHeader = NULL;



/**************Database Management********************/
Database::Database(const char* name, unsigned numPages, Status& status) {

	currentDBHeader = new DBHeader[noOfFileEnteries];

	databaseName = name;
	file.open(name, ios::out);
	file.close();
	file.open(name, ios::out | ios::in | ios::binary);
	if (file.is_open()) {
		char c[PAGE_SIZE];
		file.write(c, PAGE_SIZE); //page 0

		//write to first page,which is assigned for keeping filename entries
		file.seekg(0, ios::beg);

		strcpy(currentDBHeader[0].name, "sysTable");
		currentDBHeader[0].value = -1;//page no of sysTable
		currentDBHeader[0].flag = 'n';

		strcpy(currentDBHeader[1].name, "sysColumn");
		currentDBHeader[1].value = -1;
		currentDBHeader[1].flag = 'n';

		strcpy(currentDBHeader[2].name, "freeList");
		currentDBHeader[2].value = -1;
		freeList = -1;
		currentDBHeader[3].flag = 'n';

		strcpy(currentDBHeader[3].name, "nextEmptyPage");
		currentDBHeader[3].value = 1;
		nextEmptyPage = 1;
		currentDBHeader[3].flag = 'n';

		strcpy(currentDBHeader[4].name, "indexTable");
		currentDBHeader[4].value = -1;
		nextEmptyPage = 1;
		currentDBHeader[3].flag = 'n';

		file.write((char *) currentDBHeader, noOfFileEnteries
				* sizeof(DBHeader));
		file.flush();
		runSize = numPages;
		int y;
		allocatePage(y);
		runSize = DB_EXT_SIZE;
		status = ok;
	} else
		status = error;
}

Database::Database(const char* name, Status& status) {

	currentDBHeader = new DBHeader[noOfFileEnteries];

	file.open(name, ios::out | ios::in | ios::binary);
	if (file.is_open()) {
		file.seekg(0,ios::beg);
		file.read((char *) currentDBHeader, noOfFileEnteries * sizeof(DBHeader));
		databaseName = name;
		freeList = currentDBHeader[2].value;
		nextEmptyPage = currentDBHeader[3].value;
		runSize = DB_EXT_SIZE;
		status = ok;
	} else
		status = error;

}

Database::~Database() {
	saveState();
	file.close();
}
void Database::saveState() {
	currentDBHeader[2].value = freeList;
	currentDBHeader[3].value = nextEmptyPage;
	file.seekg(0,ios::beg);
	file.write((char *) currentDBHeader, noOfFileEnteries * sizeof(DBHeader));
	file.flush();

}
Status Database::destroy(char* name) {

	if (remove(name) != 0)
		return error;
	else
		return ok;
}

/**************Page Management********************/

Status Database::readPage(int pageNo, Page* pagePtr) {
	unsigned char *buff = (unsigned char*) malloc(PAGE_SIZE);

	file.seekg((pageNo) * PAGE_SIZE,ios::beg);
	file.read((char*)buff, PAGE_SIZE);

	pagePtr->pageno = pageNo;
	pagePtr->pageData = buff;

	return ok;
}

Status Database::writePage(int pageNo, Page* pagePtr) {
	file.seekg((pageNo) * PAGE_SIZE,ios::beg);
	file.write((char*)pagePtr->pageData, PAGE_SIZE);
	file.flush();
	return ok;
}

Status Database::allocatePage(int& startPageNum) {
	int c[PAGE_SIZE / sizeof(int)];
	if (freeList == -1) {
		file.seekg(nextEmptyPage * PAGE_SIZE, ios::beg);
		startPageNum = nextEmptyPage;
		int i;
		for (i = nextEmptyPage; i < startPageNum + runSize; i++) {
			if ((i + 1) == (startPageNum + runSize))
				c[0] = -1;
			else
				c[0] = i + 1;

			file.write((char*) c, PAGE_SIZE);
		}
		if (runSize > 1)
			freeList = startPageNum + 1;
		nextEmptyPage += runSize;

	} else {
		file.seekg(freeList * PAGE_SIZE, ios::beg);
		file.read((char*) &c, PAGE_SIZE);
		startPageNum = freeList;
		freeList = c[0];
	}
	return ok;
}

Status Database::deallocatePage(int startPageNum) {
	int c[PAGE_SIZE / (sizeof(int))];
	file.seekg(startPageNum * PAGE_SIZE, ios::beg);
	c[0] = freeList;
	file.write((char*) c, PAGE_SIZE);
	freeList = startPageNum;
	return ok;
}


/**********************General************************/

const string Database::getName() {
	return databaseName;
}

int Database::getNumPages() {
	return nextEmptyPage;
}

int Database::getPageSize() {
	return PAGE_SIZE;
}
int Database::getFreeList() {
	return freeList;
}
int Database::getNextEmptyPage() {
	return nextEmptyPage;
}

void Database::view(Page* p) {
	int j;
				for (int i = 0; i < PAGE_SIZE; i += 16) {
					for (j = 0; j < 16; j++)
						printf("%02x ", p->pageData[i + j]);
					cout << " ";
					for (j = 0; j < 16; j++)
						if (isprint(p->pageData[i + j]))
							cout << p->pageData[i + j];
						else
							cout<<"-";
					printf("\n");
				}

}


